[#escape x as (x)!?html]
  <div id="successToast" class="toast bg-success text-white position-fixed m-3 hide" style="z-index:5;right:0;top:0;" role="alert" aria-live="assertive" aria-atomic="true" data-delay="3000">
    <div class="toast-body"><i class="far fa-check-circle"></i><span class="ml-2" id="successMessage"></span></div>
  </div>

  <div id="alertToastContainer" class="position-fixed d-flex justify-content-center align-items-center" data-autohide="false"
       style="z-index:20000;background-color:rgba(0, 0, 0, 0.5);height:0;top:0;bottom:0;left:0;right:0;">
    <div id="alertToast" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true" data-delay="20000" data-autohide="false">
      <div class="toast-header p-3">
        <i class="fas fa-info-circle text-info lead mr-2"></i>
        <span id="alertMessage" class="mr-auto">操作成功</span>
        <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
    </div>
  </div>

  <script>
    $('#alertToast').on('hidden.bs.toast', function () {
      $('body').css('width', '');
      $('body').css('overflow', '');
      $('#alertToastContainer').css('height', '0');
    });

    // 检查是否需要显示提示信息
    var MESSAGE_SUCCESS = 'ujcms_message_success';
    var MESSAGE_ALERT = 'ujcms_message_alert';

    function displaySuccess(text) {
      $('#successMessage').text(text);
      $('#successToast').toast('show');
    }

    function displayAlert(text) {
      var clientWidth = document.body.clientWidth || document.documentElement.clientWidth;
      var innerWidth = window.innerWidth;
      var scrollBarWidth = innerWidth - clientWidth;
      if (scrollBarWidth > 0) {
        $('body').css('width', 'calc(100% - ' + scrollBarWidth + 'px)');
      }
      $('body').css('overflow', 'hidden');
      $('#alertToastContainer').css('height', '100%');
      $('#alertMessage').text(text);
      $('#alertToast').toast('show');
    }

    (function () {
      var messageSuccess = Cookies.get(MESSAGE_SUCCESS);
      if (messageSuccess) {
        // 显示后清空标识
        Cookies.remove(MESSAGE_SUCCESS, {path: '/'});
        displaySuccess(messageSuccess);
      }

      var messageAlert = Cookies.get(MESSAGE_ALERT);
      if (messageAlert) {
        // 显示后清空标识
        Cookies.remove(MESSAGE_ALERT, {path: '/'});
        displayAlert(messageAlert);
      }
    })();

    // 设置“操作成功”标识
    function showSuccess(text) {
      if (!text) text = '操作成功';
      Cookies.set(MESSAGE_SUCCESS, text, {path: '/'});
    }

    function showAlert(text) {
      if (!text) text = '操作成功';
      Cookies.set(MESSAGE_ALERT, text, {path: '/'});
    }
  </script>

  <div class="modal fade" id="showErrorModel" tabindex="-1" style="z-index:20000;">
    <div class="modal-dialog">
      <div class="modal-content">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <div class="modal-body"></div>
      </div>
    </div>
  </div>

  <script>
    function showError(text) {
      $('#showErrorModel .modal-dialog').removeClass('modal-xl').css('max-width', '');

      $('#showErrorModel .modal-body').text(text);
      $('#showErrorModel').modal('show');
    }

    function showErrorPreJson(json) {
      $('#showErrorModel .modal-dialog').addClass('modal-xl').css('max-width', '100%');

      var modalBody = $('#showErrorModel .modal-body').empty();
      makeModalBody(modalBody, json);
      // 不知 bootstrap 为何会自动加上 padding-right:17px，去除该项值。
      $('#showErrorModel').modal('show').css('padding-right', '');
    }

    // data 为 XMLHttpRequest 对象：https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
    function showErrorPre(data) {
      $('#showErrorModel .modal-dialog').addClass('modal-xl').css('max-width', '100%');

      var modalBody = $('#showErrorModel .modal-body').empty();
      // 能否使用 response.json() 方法？不行，只有 fetch 的 response 才可以。responseText 是 XMLHttpRequest 的标准属性。
      var json = data.responseJSON || (data.responseText && JSON.parse(data.responseText));
      if (json) {
        makeModalBody(modalBody, json);
      } else {
        modalBody.append($('<pre>').text(JSON.stringify(data, null, 4)));
      }
      // 不知 bootstrap 为何会自动加上 padding-right:17px，去除该项值。
      $('#showErrorModel').modal('show').css('padding-right', '');
    }

    function makeModalBody(modalBody, json) {
      modalBody.append($('<h1>').append($('<span>').text(json.status)).append($('<small>').text(' (' + json.error + ')')))
              .append($('<p>').text('path: ' + json.path))
              .append($('<p>').text('timestamp: ' + dayjs(json.timestamp).format('YYYY-MM-DDTHH:mm:ss.SSSZZ')))
              .append($('<p>').append($('<code>').text(json.message)));
      if (json.trace) modalBody.append($('<pre>').css({'white-space': 'pre-wrap', 'word-wrap': 'break-word'}).append($('<code>').text(json.trace)));
    }

    function handleError(data) {
      if (data.message && !data.error) {
        showError(data.message);
      } else {
        showErrorPreJson(data);
      }
    }

    var request = axios.create({timeout: 30000});
    request.interceptors.request.use(function (config) {
      var method = config.method.toLowerCase();
      if (method === 'post' || method === 'put' || method === 'delete') {
        var header = $('meta[name="_csrf_header"]').attr('content');
        var token = $('meta[name="_csrf"]').attr('content');
        config.headers = {...config.headers, [header]: token};
      }
      return config;
    }, function (error) {
      return Promise.reject(error);
    });
    request.interceptors.response.use(function (response) {
      return response;
    }, function (e) {
      console.log(e);
      var data = e.response.data;
      var status = e.response.status;
      var statusText = e.response.statusText;
      // spring boot 的响应
      if (data) {
        handleError(data);
        return Promise.reject(data.error);
      }
      // spring scurity BearerTokenAuthenticationEntryPoint 的响应
      handleError({status});
      return Promise.reject(statusText);
    });

    function fetchCsrf() {
      return axios.get('${dy}/app/csrf').then(function (response) {
        if (response.data) {
          const arr = response.data.split(',');
          if (arr.length >= 3) {
            $('meta[name="_csrf_header"]').attr('content', arr[1].trim());
            $('meta[name="_csrf"]').attr('content', arr[2].trim());
          }
        }
      });
    }
  </script>
[/#escape]
